#pragma once

#include <string>
#include <map>
#include <vector>

#include "../Drawing/Drawable.hpp"
#include "../Game/Hitbox.hpp"
#include "../Drawing/Animation.hpp"

/**
 * \class Entity
 * \brief A physic, drawable entity.
 * \details This abstract class manages the displacement, life and state of an entity. It can also be a very basic event (a door, a warp...)
 * \author Xerxes
 * \version 0.2
 * \date 20/02/2013 - 22/02/2013
 * \see Character, Player
 */

class Entity : public Drawable
{
public:
//----------------------------------------------------------
//					CONSTRUCTION
//----------------------------------------------------------
	/**
	 * \fn Entity(unsigned int nid = -1);
	 * \brief Create an Entity with the given ID
	 */
	Entity(std::string nid);
	Entity(Entity& clone);
	virtual ~Entity(void);

	enum State{IDLE_DOWN, IDLE_LEFT, IDLE_RIGHT, IDLE_UP, WALK_DOWN, WALK_LEFT, WALK_RIGHT, WALK_UP, STATES_COUNT};
	enum EntityType{UNDEFINED, CHARACTER, PLAYER, DOOR, MONSTER, ENTITYTYPECOUNT};

//----------------------------------------------------------
//					STATIC METHODS
//----------------------------------------------------------
	/**
	 * \fn static void updateAll()
	 * \brief Causes every entity in the game to update.
	 */
	static void UpdateAll();
	/**
	 * \fn static Entity* Find_by_id(int id)
	 * \brief find an entity when you have its ID.
	 * \return the entity of the current ID. If not entity was found, return NULL.
	 */
	static Entity* find_by_id(int id);
	static std::vector<Entity*> find_by_name(std::string name);
	static std::string last_ID(){return Entity::last_id;}

	static void clearAll();

//----------------------------------------------------------
//					INTERRACTION
//----------------------------------------------------------
	virtual void Update();
	virtual void Draw();
	virtual void DebugDraw();
	

//----------------------------------------------------------
//					ACCESS
//----------------------------------------------------------
	/**
	 * \fn int id()
	 * The ID is given at the creation, cannot be changed
	 * \return the entity's ID
	 */
	std::string id() const {return m_id;}

	/**
	 * \fn std::string Name() const
	 * Given name, for dialogs or whatever
	 * \return the entity's name
	 */
	std::string name() const {return m_name;}
	void setName(std::string nname) {m_name = nname;}
	

	/**
	 * \fn bool IsSolid() const
	 * \brief Specify if the player or another entity can get through this entity.
	 * \details Still, even being non solid the entity cannot walk through a wall
	 * \see SetSolid() IsGhost()
	 */
	bool isSolid() const {return m_solid;}
	void setSolid(bool onoff){m_solid = onoff;}

	/**
	 * \fn bool IsGhost() const
	 * \brief Specify if the entity can walk through the walls
	 * \details Still, even being a ghost the entity cannot walk through other entity. Use SetGhost AND SetSolid to do so.
	 * \see SetGhost() IsSolid()
	 */
	bool isGhost() const {return m_ghost;}
	void setGhost(bool onoff){m_ghost = onoff;}

	/**
	 * \fn friend std::ostream& operator<<(std::ostream& stream, Entity ent)
	 * \brief Debug method for entity console display
	 */
	friend std::ostream& operator<<(std::ostream& stream, Entity const& ent);

//----------------------------------------------------------
//				LIFE MANAGEMENT
//----------------------------------------------------------
	int life() const {return m_life;}	
	void setLife(unsigned int value){m_life = value;}
	void setMaxLife(unsigned int value){m_max_life = value;m_life = (m_life > m_max_life ? m_max_life : m_life);}
	/**
	 * \fn void Hurt(int damage)
	 * \brief Hurts the entity with the given amount of damage. If the value is negative, heals the entity
	 */
	void hurt(int damage){m_life = (damage >= (int)m_life ? 0 : ((int)m_life - damage > (int)m_max_life ? m_max_life : m_life - damage));}
	/**
	 * \fn void Kill()
	 * \brief Instant kill the entity, setting the life to 0.
	 */
	void kill(){m_life = 0;}
	/**
	 * \fn bool isAlive() const
	 * \brief Checks if the life of the entity is > 0
	 * \return True if life > 0, false otherwise
	 */
	bool isAlive() const {return m_life > 0;}

//----------------------------------------------------------
//				Cartesian coordinate
//----------------------------------------------------------

	/**
	 * \fn float X()
	 * \brief X coordinate on the map.
	 * \details Important : this is the top left corner of the sprite, not the central position of the entity.
	 * To get the central coordinate, use CentralX() instead
	 * \return the x position
	 * \see CentralX()
	 */
	float x(){return m_x_pos;}
	/**
	 * \fn float Y()
	 * \brief Y coordinate on the map.
	 * \details Important : this is the top left corner of the sprite, not the central position of the entity.
	 * To get the central coordinate, use CentralY() instead
	 * \return the y position
	 * \see CentralY()
	 */
	float y(){return m_y_pos;}
	/**
	 * \fn float CentralX()
	 * \brief Central coordinate
	 * \return the central X position according to the current hitbox
	 */
	float centralX(){return m_hitbox.centerX();}
	/**
	 * \fn float CentralY()
	 * \brief Central coordinate
	 * \return the central Y position according to the current hitbox
	 */
	float centralY(){return m_hitbox.centerY();}

//----------------------------------------------------------
//				COLLISION
//----------------------------------------------------------
	// TODO
	bool collide(Entity& ent);
	const Hitbox& getHitbox() const;

//----------------------------------------------------------
//				EVENTS
//----------------------------------------------------------
	
	void on_Entity_Collide(Entity& ent);
	void on_Player_Interraction(Entity& player);

//----------------------------------------------------------
//				Displacements
//----------------------------------------------------------

	void setX(float x){m_x_pos = x;}
	void setY(float y){m_y_pos = y;}
	void setPosition(float x, float y){m_y_pos = y;m_x_pos = x; m_hitbox.setPos(m_x_pos, m_y_pos);m_anims[m_current_state]->sprite().setPosition(x,y);}
	
	// TODO
	/**
	 * \fn bool Move()
	 * \brief Instant move according to a (x,y) vector
	 * \details Teleport to the location (current_x + x, current_y + y)
	 * \param x : horizontal vector increment.
	 * \param y : vertical vector increment.
	 * \param force : teleport even if the entity cannot fit here.
	 * \return false if the entity cannot teleport here, true otherwise.
	 */
	bool move(float x, float y, bool force = false){}
//----------------------------------------------------------
//				Advanced displacements
//----------------------------------------------------------

	// TODO : DisplacementManager ?

	/**
	 * \fn bool GoTo()
	 * \brief Try a basic displacement from current location to another point of the map
	 * \details TODO : explain method
	 * \param x_global : x location on the map
	 * \param y_global : y location on the map
	 * \return false when the entity is stuck
	 */
	bool goTo(float x_global, float y_global); 
	
	/**
	 * \fn bool FindPathTo()
	 * \brief More sophysticated way to move to a location
	 * TODO : explain method
	 * \return false if the location cannot be reached
	 */
	bool findPathTo(float x_global, float y_global);

private:
	std::string m_id;

	static std::string last_id;
	static std::map<std::string, Entity*> all_entities;
protected:
	std::string m_name;
	
	unsigned int m_life;
	unsigned int m_max_life;
	std::map<State, Animation*> m_anims;
	//Animation anim;

	State m_current_state;

	float m_vel_x, m_vel_y;
	float m_x_pos, m_y_pos; 

	bool m_solid, m_ghost;

	Hitbox m_hitbox;

	EntityType m_type;
};


